/*
    Copyright 2006 Todi.
    Send feedback and suggestions to todi@peerfeeds.com.  
  
    This file is part of Peerfeeds.

    Peerfeeds is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    Peerfeeds is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Peerfeeds; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA 
  
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Collections;
using System.Data;
using System.Text;
using System.Net;
using System.IO;
using System.Xml;
using System.Text.RegularExpressions;
using System.Configuration;

namespace Peerfeeds
{   
    class Rss
    {
        private XmlNode nodeRss;
        private XmlNode nodeChannel;
        private XmlNode nodeItem;
        //public DateTime lastModified;

        public ArrayList RssReader(string url, int items, ref DateTime lastModified)
        {
            // Create a new XmlTextReader from the specified URL (RSS feed)
            string data = GetURL(url, ref lastModified);
            
            // Create an ArrayList to store the feed in
            ArrayList list = new ArrayList();
            Hashtable ha = new Hashtable();

            XmlTextReader rssReader = new XmlTextReader(new StringReader(data));

            // Load the XML content into a XmlDocument
            XmlDocument rssDoc = new XmlDocument();
            rssDoc.Load(rssReader);

            // Since we won't be storing more than items + header, we can set capacity right away (tiny optimization)
            list.Capacity = (items + 1);
            string name = "";
            
            // Loop for the <rss>, <rdf:RDF> or <feed> tag
            for (int i = 0; i < rssDoc.ChildNodes.Count; i++)
            {
                // If it is the rss tag
                name = rssDoc.ChildNodes[i].Name;
                string cont = rssDoc.ChildNodes[i].NodeType.ToString();
                string test1 = rssDoc.ChildNodes[i].InnerText.ToString();
                string test2 = rssDoc.ChildNodes[i].HasChildNodes.ToString();

                if ((name == "rss" || name == "rdf:RDF" || name == "feed") && (rssDoc.ChildNodes[i].NodeType == XmlNodeType.Element))
                {
                    // <rss> tag found
                    nodeRss = rssDoc.ChildNodes[i];
                    break;
                }
            }

            //  TODO: Add some stats over what kind of rss the feeds are. Base on rootnode.
            // <rss version="0.91">
            // <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
            // <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://purl.org/rss/1.0/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:syn="http://purl.org/rss/1.0/modules/syndication/" xmlns:admin="http://webns.net/mvcb/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">
            // <feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-US">

            // Need a little special handling here to be able to manage atomfeeds
            if (name != "feed")
            {
                // Loop for the <channel> tag
                for (int i = 0; i < nodeRss.ChildNodes.Count; i++)
                {
                    // If it is the channel tag it's rss
                    if (nodeRss.ChildNodes[i].Name == "channel")
                    {
                        // <channel> tag found
                        nodeChannel = nodeRss.ChildNodes[i];
                        break;
                    }
                }
            }
            else
            {
                nodeChannel = nodeRss;
            }

            // We store the variables in a hashtable for the header
            ha = new Hashtable();
            int place = 0;

            for (int x = 0; x < nodeChannel.ChildNodes.Count; x++)
            {
                name = nodeChannel.ChildNodes[x].Name;
                if (name == "item" || name == "entry") 
                {
                    place = x;
                    break;
                }

                ha["%" + nodeChannel.ChildNodes[x].Name + "%"] = nodeChannel.ChildNodes[x].InnerText;

                // Get attributes
                if (nodeChannel.ChildNodes[x].Attributes != null && nodeChannel.ChildNodes[x].Attributes.Count > 0)
                {
                    for (int xx = 0; xx < nodeChannel.ChildNodes[x].Attributes.Count; xx++)
                    {
                        ha["%" + nodeChannel.ChildNodes[x].Name + "[" + nodeChannel.ChildNodes[x].Attributes[xx].Name +
                           "]%"] = nodeChannel.ChildNodes[x].Attributes[xx].InnerText;
                    }
                }
            }
            list.Add(ha);

            // Need to do this a little differently if the feed is an RDF one, because it's structured a little differently
            if (nodeRss.Name == "rdf:RDF")
            {
                nodeChannel = nodeRss;
            }

            // Need a counter here
            int itemCounter = 0;

            // Loop for the <title>, <link>, <description> and all the other tags
            for (int i = place; i < nodeChannel.ChildNodes.Count; i++)
            {
                // If it is the item tag, then it has children tags which we will add as items
                name = nodeChannel.ChildNodes[i].Name;

                if (name == "item" || name == "entry") // entry is for atom feeds
                {
                    nodeItem = nodeChannel.ChildNodes[i];
                    itemCounter++;
                    
                    // Retrieve the information contained in the node and store it in a hashtable
                    ha = new Hashtable();
                   
                    for (int x = 0; x < nodeItem.ChildNodes.Count; x++)
                    {
                        ha["%" + nodeItem.ChildNodes[x].Name + "%"] = nodeItem.ChildNodes[x].InnerText;

                        // Get attributes if there are any
                        for (int xx = 0; xx < nodeItem.ChildNodes[x].Attributes.Count; xx++)
                        {
                            ha["%" + nodeItem.ChildNodes[x].Name + "[" + nodeItem.ChildNodes[x].Attributes[xx].Name +
                                "]%"] = nodeItem.ChildNodes[x].Attributes[xx].InnerText;
                        }

                        // Get childnodes if there are any (usually not, but atleast atom feeds have them)
                        for (int xx = 0; xx < nodeItem.ChildNodes[x].ChildNodes.Count; xx++)
                        {
                            if (nodeItem.ChildNodes[x].ChildNodes[xx].Name != "#text")
                            {
                                ha["%" + nodeItem.ChildNodes[x].Name + "[" + nodeItem.ChildNodes[x].ChildNodes[xx].Name +
                                    "]%"] = nodeItem.ChildNodes[x].ChildNodes[xx].InnerText;
                            }
                        }

                    }

                    // Add to list
                    list.Add(ha);
                }
                
                // We've retrieved all the items we wanted so lets quit
                if (items <= itemCounter)
                {
                    break;
                }
            }
            
            return list;
        }


        /* 
         This code was borrowed from Microsoft:
         http://msdn2.microsoft.com/en-us/library/system.net.httpwebrequest.useragent.aspx
        */
        private string GetURL(string txtUrl, ref DateTime lastModified)
        {
            StringBuilder content = new StringBuilder();

            // Create a new 'HttpWebRequest' object to the mentioned URL.
            HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(txtUrl);
            myHttpWebRequest.UserAgent = "Peerfeeds/1.1 (Open Source RSS-Bot written in C#; +http://www.peerfeeds.com)";
            if (lastModified != null)
            { 
                myHttpWebRequest.IfModifiedSince = lastModified;
            }

            myHttpWebRequest.KeepAlive = false;
            myHttpWebRequest.AllowAutoRedirect = true;

            // Assign the response object of 'HttpWebRequest' to a 'HttpWebResponse' variable.
            HttpWebResponse myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();

            // Get the contents of the page
            Stream streamResponse = myHttpWebResponse.GetResponseStream();
            StreamReader streamRead = new StreamReader(streamResponse, Encoding.Default, true);
            Char[] readBuff = new Char[256];
            int count = streamRead.Read(readBuff, 0, 256);

            while (count > 0)
            {
                content.Append(new String(readBuff, 0, count));
                count = streamRead.Read(readBuff, 0, 256);
            }

            // Release the response object resources.
            lastModified = myHttpWebResponse.LastModified;

            streamRead.Close();
            streamResponse.Close();
            myHttpWebResponse.Close();

            return content.ToString();
        }
    }
}
